/**
  ETFDAQ Project -- online from ETFAna Online by SUN Yazhou
  \class TAVClient
  \brief client for communication among different hosts on the Internet
  NOTE that this is an abstract class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2023-06-09
  \date 2023-06-09 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                    </tr>
  <tr>  <td>2023-06-09   <td>Asia Sun    <td>file created                   </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include "TAVClient.h"
#include "TAQueue.h"
#include "TAMsg.h"

using std::cout;
using std::endl;

#define er TAMsg::Error

TAVClient::TAVClient() : TADaqData(), fd(-1){}

/// establish connection with ip:port
TAVClient::TAVClient(const string &ip, short port, DaqType daqType)
: TADaqData(daqType), fIP(ip), fPort(port), fd(-1){
  fIsConnected = Connect();
} // end ctor

TAVClient::~TAVClient(){
  Close();
} // end dtor

// establish connection with ip:port
bool TAVClient::Connect(){
  // fd = socket(AF_INET, SOCK_STREAM, 0);
  fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // NON_BLOCK
  if(-1 == fd) er("TAVClient", "Connect: could not create socket: %s", strerror(errno));
  TAMsg::Info("TAVClient", "Connect(%s): socket created, file-dscpt: %d", fDaqC, fd);

  fSock.sin_family = AF_INET;
  fSock.sin_addr.s_addr = inet_addr(fIP.c_str());
  fSock.sin_port = htons(fPort); // host to net (short) byte order

  // time out waiting for the emergence of the object server //
  const int DT = 200;
  time_t t0 = TAMsg::ms(), t1 = t0, dt = DT;
  TAMsg::Info("TAVClient", "Connect(%s): Waiting for a server", fDaqC);
  while(-1 == connect(fd, (sockaddr *)&fSock, sizeof(fSock))){
    if(TAMsg::ms() - t1 >= 50){ // print every 0.05 s
      cout << "About to time out: " << dt << "ms\r" << std::flush;
      dt -= 100; t1 = TAMsg::ms();
    } // end if
    if(TAMsg::ms() - t0 > DT){
      TAMsg::Info("TAVClient(%s)", "Connection timed out, abort...", fDaqC);
      return false;
    } // end if
    if(TAMsg::irp()) return false;
  } // end while
  TAMsg::Info("TAVClient", "Connect(%s): connection to %s:%d established",
    fDaqC, fIP.data(), fPort);

  // read the greeting of the server to acknowledge the connection //
  char m0[128] = "";
  ssize_t s = 0, p = 0;
  while(1){ // unblock read
    s = read(fd, m0 + p, sizeof(m0) - p);
    if(-1 == s && EAGAIN == errno) continue;
    if(s > 0) p += s;
    if(size_t(p) <= sizeof(m0)) break;
  } // end while
  if(-1 == s) er("TAVClient", "Connect: reading ack error: %s", strerror(errno));
  puts(m0);
  return true;
} // end member function Connect

// thread func to read from LAN
void *TAVClient::avatar(void *arg){
  TAVClient *vc = static_cast<TAVClient *>(arg);
  while(!TAMsg::irp()) vc->recv();
  return nullptr;
} // end member function avatar
void TAVClient::Initialize(){
  const int s = pthread_create(&fRecv, nullptr, TAVClient::avatar, this);
  fIsRecvThAlive = true;
  if(0 != s) er("TAVClient(%s)", "Initialize: pthread_create err - %s", fDaqC, strerror(s));
  TAMsg::Info("TAVClient(%s)", "Initialize: the receive thread is now online", fDaqC);
} // end member function Initialize

// do some clean-ups
void TAVClient::Close(){
  // close the recv thread //
  if(fIsRecvThAlive){
    pthread_cancel(fRecv);
    const int s = pthread_join(fRecv, nullptr);
    if(0 != s) er("TAVClient", "Close: pthread_join err - %s", strerror(s));
    fIsRecvThAlive = false;
  } // end if
  // close the sock //
  if(fd >= 0){ close(fd); fd = -1; }

  printf("\033[36;1mThis is %s client. Bye~\033[0m\n", fDaqC);
  if(fQueue){ delete fQueue; fQueue = nullptr; }
} // end member function Close
